package cn.yiynx.example.service.impl;

import java.util.*;
import java.util.concurrent.*;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;

import cn.yiynx.example.entity.Demo;
import cn.yiynx.example.mapper.DemoMapper;
import cn.yiynx.example.util.HttpResponseUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.OutputStream;

/**
 * 基于游标查询的Excel导出服务（支持浏览器下载）
 * @author yl
 */
@Service
public class ExcelExporter {
    @Resource
    private DemoMapper demoMapper;
    // 核心线程数
    private static final int CORE_POOL_SIZE = 4;
    // 最大线程数
    private static final int MAX_POOL_SIZE = 8;
    // 队列容量
    private static final int QUEUE_CAPACITY = 1000;
    // 每批次查询数量
    private static final int BATCH_SIZE = 5000;
    // 任务超时时间(毫秒)
    private static final long TASK_TIMEOUT_MS = 60000;
    // Excel单个Sheet最大行数 (Excel 2007+)
    private static final int EXCEL_MAX_ROWS_PER_SHEET = 1048575;

    /**
     * 导出数据到Excel文件并提供浏览器下载
     * @param response HttpServletResponse对象
     * @param fileName 下载的文件名（不含扩展名）
     */
    public void exportToExcel(HttpServletResponse response, String fileName) throws InterruptedException, ExecutionException, IOException {
        // 创建线程池
        ExecutorService executorService = new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAX_POOL_SIZE,
            60L,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(),
            new ThreadPoolExecutor.CallerRunsPolicy()
        );

        // 获取最大和最小ID，确定游标查询范围
        int[] idRange = getMinMaxId();
        int minId = idRange[0];
        int maxId = idRange[1];
        final int totalBatches = (maxId - minId + BATCH_SIZE) / BATCH_SIZE;

        System.out.println("导出数据总数预估: " + (maxId - minId + 1) + ", 分批次: " + totalBatches);

        // 阻塞队列用于存储带批次号的数据块
        final BlockingQueue<DataBatch> dataQueue = new LinkedBlockingQueue<>(QUEUE_CAPACITY);
        // 用于临时存储已完成但未按顺序处理的批次数据
        final ConcurrentHashMap<Integer, List<Demo>> pendingBatches = new ConcurrentHashMap<>();

        // 设置响应头并获取输出流
        OutputStream outputStream = null;
        try {
            outputStream = HttpResponseUtil.excelOutput(response, fileName);
            // 创建不关闭的包装流，防止EasyExcel关闭HTTP响应流
            final NonCloseableOutputStream nonCloseableOutputStream = new NonCloseableOutputStream(outputStream);

            // 启动消费者线程（Excel写入）
            CompletableFuture<Void> consumerFuture = CompletableFuture.runAsync(() -> {
                try {
                    writeToExcel(nonCloseableOutputStream, totalBatches, dataQueue, pendingBatches);
                } catch (Exception e) {
                    System.err.println("Excel写入失败: " + e.getMessage());
                    e.printStackTrace();
                    // 放入结束标记，确保消费者不会无限等待
                    try {
                        dataQueue.put(new DataBatch(-1, null));
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                    }
                }
            }, executorService);

            // 启动生产者线程（游标数据查询）
            List<CompletableFuture<Void>> producerFutures = new ArrayList<>();
            for (int i = 0; i < totalBatches; i++) {
                final int batchNum = i;
                final int startId = minId + (batchNum * BATCH_SIZE);
                final int endId = Math.min(minId + ((batchNum + 1) * BATCH_SIZE) - 1, maxId);

                producerFutures.add(CompletableFuture.runAsync(() -> {
                    try {
                        List<Demo> dataList = queryDataByCursor(startId, endId);
                        // 按批次号放入队列
                        dataQueue.put(new DataBatch(batchNum, dataList));
                    } catch (Exception e) {
                        System.err.println("查询批次" + batchNum + "失败: " + e.getMessage());
                        e.printStackTrace();
                        try {
                            // 发生错误时放入空数据，确保流程能继续
                            dataQueue.put(new DataBatch(batchNum, new ArrayList<>()));
                        } catch (InterruptedException ie) {
                            Thread.currentThread().interrupt();
                        }
                    }
                }, executorService));
            }

            // 等待所有生产者完成
            CompletableFuture.allOf(producerFutures.toArray(new CompletableFuture[0])).thenRun(() -> {
                try {
                    // 放入结束标记
                    dataQueue.put(new DataBatch(-1, null));
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });

            // 等待消费者完成
            consumerFuture.get();

            // 刷新输出流，确保所有数据都写入响应
            outputStream.flush();

        } finally {
            // 关闭线程池
            executorService.shutdown();
            try {
                executorService.awaitTermination(5, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    /**
     * 写入数据到Excel文件（支持多Sheet）
     */
    private void writeToExcel(OutputStream outputStream, int totalBatches,
                              BlockingQueue<DataBatch> dataQueue,
                              ConcurrentHashMap<Integer, List<Demo>> pendingBatches) {
        int nextExpectedBatch = 0;
        int currentSheetIndex = 0;
        int currentRowCount = 0;
        boolean isFirstSheet = true;

        ExcelWriter excelWriter = null;
        try {
            // 创建ExcelWriter，禁用自动关闭流
            excelWriter = EasyExcel.write(outputStream, Demo.class).autoCloseStream(false).build();
            WriteSheet writeSheet = EasyExcel.writerSheet(currentSheetIndex, "Sheet" + (currentSheetIndex + 1)).build();

            while (true) {
                DataBatch dataBatch = dataQueue.poll(TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
                if (dataBatch == null) {
                    System.err.println("任务超时，等待数据超时");
                    break;
                }

                // 检测结束标记
                if (dataBatch.batchNum == -1) {
                    break;
                }

                // 处理数据批次
                if (dataBatch.batchNum == nextExpectedBatch) {
                    // 批次顺序正确，可以直接写入
                    if (dataBatch.dataList != null && !dataBatch.dataList.isEmpty()) {
                        writeDataListToExcel(excelWriter, writeSheet, dataBatch.dataList,
                            currentRowCount, currentSheetIndex, isFirstSheet);
                        currentRowCount += dataBatch.dataList.size();

                        // 检查是否需要创建新Sheet
                        if (currentRowCount >= EXCEL_MAX_ROWS_PER_SHEET) {
                            currentSheetIndex++;
                            isFirstSheet = false;
                            currentRowCount = 0;
                            writeSheet = EasyExcel.writerSheet(currentSheetIndex, "Sheet" + (currentSheetIndex + 1))
                                .needHead(isFirstSheet)
                                .build();
                        }
                    }
                    nextExpectedBatch++;

                    // 检查是否有等待中的后续批次可以处理
                    while (pendingBatches.containsKey(nextExpectedBatch)) {
                        List<Demo> pendingData = pendingBatches.remove(nextExpectedBatch);
                        if (pendingData != null && !pendingData.isEmpty()) {
                            writeDataListToExcel(excelWriter, writeSheet, pendingData,
                                currentRowCount, currentSheetIndex, isFirstSheet);
                            currentRowCount += pendingData.size();

                            // 检查是否需要创建新Sheet
                            if (currentRowCount >= EXCEL_MAX_ROWS_PER_SHEET) {
                                currentSheetIndex++;
                                isFirstSheet = false;
                                currentRowCount = 0;
                                writeSheet = EasyExcel.writerSheet(currentSheetIndex, "Sheet" + (currentSheetIndex + 1))
                                    .needHead(isFirstSheet)
                                    .build();
                            }
                        }
                        nextExpectedBatch++;
                    }
                } else {
                    // 批次顺序不正确，先放入待处理队列
                    pendingBatches.put(dataBatch.batchNum, dataBatch.dataList);
                }
            }

            System.out.println("Excel写入完成，共处理批次: " + nextExpectedBatch + "/" + totalBatches + ", 创建Sheet数: " + (currentSheetIndex + 1));

            // 调用excelWriter.finish()完成Excel文件构建，但不会关闭底层流
            if (excelWriter != null) {
                try {
                    // 关键修改：调用finish()方法完成Excel文件构建
                    excelWriter.finish();
                } catch (Exception e) {
                    System.err.println("ExcelWriter操作失败: " + e.getMessage());
                }
            }
        } catch (Exception e) {
            System.err.println("Excel写入失败: " + e.getMessage());
            e.printStackTrace();
        } finally {
            // 不需要额外关闭操作，因为我们使用了autoCloseStream(false)
            // 底层流由Servlet容器管理
        }
    }

    /**
     * 将数据列表写入Excel，处理跨Sheet的情况
     */
    private void writeDataListToExcel(ExcelWriter excelWriter, WriteSheet writeSheet,
                                      List<Demo> dataList, int currentRowCount,
                                      int currentSheetIndex, boolean isFirstSheet) {
        int dataSize = dataList.size();

        // 检查是否需要分跨Sheet
        if (currentRowCount + dataSize > EXCEL_MAX_ROWS_PER_SHEET) {
            // 计算当前Sheet还能写入多少行
            int rowsRemaining = EXCEL_MAX_ROWS_PER_SHEET - currentRowCount;

            if (rowsRemaining > 0) {
                // 先写入当前Sheet剩余空间
                List<Demo> partialData = dataList.subList(0, rowsRemaining);
                excelWriter.write(partialData, writeSheet);
            }

            // 创建新Sheet
            int newSheetIndex = currentSheetIndex + 1;
            WriteSheet newSheet = EasyExcel.writerSheet(newSheetIndex, "Sheet" + (newSheetIndex + 1))
                .needHead(false) // 新Sheet不写表头
                .build();

            // 写入剩余数据到新Sheet
            if (rowsRemaining < dataSize) {
                List<Demo> remainingData = dataList.subList(rowsRemaining, dataSize);
                excelWriter.write(remainingData, newSheet);
            }
        } else {
            // 不需要跨Sheet，直接写入
            excelWriter.write(dataList, writeSheet);
        }
    }

    /**
     * 获取最小和最大ID
     */
    private int[] getMinMaxId() {
        // 获取最小ID
        LambdaQueryWrapper<Demo> minWrapper = Wrappers.lambdaQuery(Demo.class)
            .select(Demo::getId)
            .orderByAsc(Demo::getId)
            .last("LIMIT 1");
        Demo minDemo = demoMapper.selectOne(minWrapper);
        int minId = minDemo != null ? minDemo.getId() : 0;

        // 获取最大ID
        LambdaQueryWrapper<Demo> maxWrapper = Wrappers.lambdaQuery(Demo.class)
            .select(Demo::getId)
            .orderByDesc(Demo::getId)
            .last("LIMIT 1");
        Demo maxDemo = demoMapper.selectOne(maxWrapper);
        int maxId = maxDemo != null ? maxDemo.getId() : 0;

        return new int[]{minId, maxId};
    }

    /**
     * 使用游标方式查询数据
     */
    private List<Demo> queryDataByCursor(int startId, int endId) {
        LambdaQueryWrapper<Demo> queryWrapper = Wrappers.lambdaQuery(Demo.class)
            .ge(Demo::getId, startId)
            .le(Demo::getId, endId)
            .orderByAsc(Demo::getId); // 确保每个批次内部的数据是有序的
        return demoMapper.selectList(queryWrapper);
    }

    /**
     * 数据批次内部类，用于标识批次号和对应的数据
     */
    private static class DataBatch {
        int batchNum; // 批次号
        List<Demo> dataList; // 数据列表

        public DataBatch(int batchNum, List<Demo> dataList) {
            this.batchNum = batchNum;
            this.dataList = dataList;
        }
    }

    /**
     * 不关闭的OutputStream包装类，防止底层流被关闭
     */
    private static class NonCloseableOutputStream extends OutputStream {
        private final OutputStream delegate;

        public NonCloseableOutputStream(OutputStream delegate) {
            this.delegate = delegate;
        }

        @Override
        public void write(int b) throws IOException {
            delegate.write(b);
        }

        @Override
        public void write(byte[] b) throws IOException {
            delegate.write(b);
        }

        @Override
        public void write(byte[] b, int off, int len) throws IOException {
            delegate.write(b, off, len);
        }

        @Override
        public void flush() throws IOException {
            delegate.flush();
        }

        @Override
        public void close() throws IOException {
            // 空实现，不关闭底层流
            // 底层流由Servlet容器管理
        }
    }
}